package bancosys.tec.rtm.impl.diagnostician;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;

import org.hibernate.EntityMode;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.hibernate.metadata.ClassMetadata;

import bancosys.tec.rtm.diagnostician.DiagnosisBean;

/**
 * Diagnostician para medir a latencia com o banco. Usa o hibernate para tal, de modo que o tempo computado contem o tempo que o hibernate
 * demora para materializar as linhas do banco em objetos de negocio.
 * 
 * @author takeshi
 */
public class DataBasePerformanceDiagnostician extends AbstractPerformanceDiagnostician {

    private final Class<?> entityType;

    private final Serializable entityId;

    private final SessionFactory sessionFactory;

    /**
     * C'tor
     * 
     * @param entityType o tipo da entidade. Nao pode ser null
     * @param entityId o id da entidade. Se for null, vai trazer todos os objetos daquele tipo
     * @param factory {@link SessionFactory}
     */
    public DataBasePerformanceDiagnostician(Class<?> entityType, Serializable entityId, SessionFactory factory) {
        this.entityType = entityType;
        this.entityId = entityId;
        this.sessionFactory = factory;
    }

    /**
     * C'tor que traz todos os objetos do tipo passado.
     * 
     * @param entityType o tipo da entidade. Nao pode ser null.
     * @param factory {@link SessionFactory}
     */
    public DataBasePerformanceDiagnostician(Class<?> entityType, SessionFactory factory) {
        this(entityType, null, factory);
    }

    /**
     * C'tor que escolhe algum tipo de entidade arbitrariamente e devolve todos os objetos daquele tipo.
     * 
     * @param factory {@link SessionFactory}
     */
    public DataBasePerformanceDiagnostician(SessionFactory factory) {
        this(selectEntity(factory), null, factory);
    }

    /**
     * Seleciona uma entidade 'ao acaso' - devolve a primeira entidade da lista de entidades mapeadas do hibernate
     * 
     * @param factory {@link SessionFactory}
     * @return {@link Class}
     */
    private static Class<?> selectEntity(SessionFactory factory) {
        ClassMetadata meta = (ClassMetadata) factory.getAllClassMetadata().values().iterator().next();
        return meta.getMappedClass(EntityMode.POJO);
    }

    /**
     * {@inheritDoc}
     */
    public String getName() {
        return "DatabasePerformanceDiagnostician: " + this.entityType + (entityId == null ? "" : " - " + entityId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected long performOperationCount(DiagnosisBean bean) throws Exception {
        Session session = this.sessionFactory.getCurrentSession();
        List<?> objects = this.fecthEntities(session);
        bean.addDescription("Entidades do tipo: " + entityType + " encontradas: " + objects.size());
        return objects.size();
    }

    /**
     * Procura as entidades propriamente ditas
     * 
     * @param session {@link Session}
     * @return {@link List}
     */
    protected List<?> fecthEntities(Session session) {
        if (entityId == null) {
            return session.createCriteria(entityType).list();
        } else {
            return Collections.singletonList(session.get(this.entityType, this.entityId));
        }
    }

}
